package xin.marcher.wind.process.config;

import org.dom4j.Document;
import org.dom4j.Element;
import xin.marcher.wind.process.enums.InvokeMethod;
import xin.marcher.wind.process.model.BizConfigModel;
import xin.marcher.wind.process.model.ProcessModel;
import xin.marcher.wind.process.model.ProcessNodeModel;

import java.util.ArrayList;
import java.util.List;

import static xin.marcher.wind.process.constants.ProcessConstants.*;

/**
 * @author marcher
 */
public abstract class AbstractXmlProcessParser implements ProcessParser {

    @Override
    public List<ProcessModel> parse() throws Exception {
        Document document = getDocument();
        Element rootElement = document.getRootElement();
        // 针对 root 标签，可以拿到子标签
        List<Element> processElements = rootElement.elements();

        // 把每个 <process> 标签以及他子标签的内容，解析封装为对应的 ProcessModel 数据模型
        List<ProcessModel> processModels = new ArrayList<>();

        // 遍历到每个 <process> 标签
        for (Element process : processElements) {
            ProcessModel model = new ProcessModel();
            model.setName(process.attributeValue(NAME_ATTR));

            // 获取 <process> 标签的子标签 list，<node> 标签集合
            List<Element> elements = process.elements();
            for (Element element : elements) {
                if(NODES_ELEMENT.equals(element.getName())) {
                    processNodesElement(element, model);
                    // <process> 标签对应的 Model 就放到一个 list 集合里去
                    processModels.add(model);
                }
                else if(BIZ_RELATIONS_ELEMENT.equals(element.getName())) {
                    processBizRelationsElement(element, model);
                }
            }
        }

        return processModels;
    }

    private void processNodesElement(Element nodesElement, ProcessModel model) {
        List<Element> nodeElements = nodesElement.elements();
        for (Element node : nodeElements) {
            // 每个 <node> 标签都要封装为对应的 ProcessNodeModel 数据模型
            ProcessNodeModel processNodeModel = new ProcessNodeModel();
            processNodeModel.setName(node.attributeValue(NAME_ATTR));
            processNodeModel.setClassName(node.attributeValue(CLAZZ_ATTR));
            String next = node.attributeValue(NEXT_ATTR);
            if (next != null) {
                processNodeModel.setNextNode(next);
            }
            String begin = node.attributeValue(BEGIN_ATTR);
            processNodeModel.setBegin(Boolean.parseBoolean(begin));
            String invokeMethodStr = node.attributeValue(INVOKE_METHOD_ATTR);
            InvokeMethod invokeMethod = invokeMethodStr == null ? InvokeMethod.SYNC :
                    InvokeMethod.valueOf(invokeMethodStr.toUpperCase());
            processNodeModel.setInvokeMethod(invokeMethod);
            model.addNode(processNodeModel);
        }
    }

    private void processBizRelationsElement(Element bizRelationsElement, ProcessModel model) {
        List<Element> bizConfigElements = bizRelationsElement.elements();
        for (Element bizConfig : bizConfigElements) {
            BizConfigModel bizConfigModel = new BizConfigModel();
            bizConfigModel.setName(bizConfig.attributeValue(NAME_ATTR));
            bizConfigModel.setBusinessIdentifier(Integer.valueOf(bizConfig.attributeValue(BIZ_ID_ATTR)));
            bizConfigModel.setOrderType(Integer.valueOf(bizConfig.attributeValue(ORDER_TYPE_ATTR)));
            model.addBizConfig(bizConfigModel);
        }
    }

    protected abstract Document getDocument() throws Exception;

}
